var a = 0;
if (true) {
a = 1;
function a() {return 3}
a = 2;
console.log(a);
}
console.log(a);
问题是问两个打印机处的 a 会是什么?
直觉上我们可能会答:2 和 2,可是在我们熟悉的 chrome 中,结果居然是 2 和 1。
其实还有人说过,在 safari 中,结果是 2 和 2。没想到,最懂我的还是 safari。
我自己额外还发现了两种不同情况,如果加上严格模式,结果是 2 和 0;在带有编译环境的情况下,执行结果会是 2 和 ƒ () { return 3; }
。
这段代码既然我们可能怎么答都不可能答对,不如贬低一下它。
关于这里的第三点,我们可以从 MDN 上找到说法: “这种声明方式在不同的浏览器里可能有不同的效果”。
我们也就可以稍稍理解为什么 chrome 和 safari 的不同结果了。
其实我如果我们真的想弄明白执行结果,可以采用单步执行查看调用时产生的作用域变量就可以。
针对有编译环境的代码,我们可以直接查看其编译结果,这个相对容易,我们先说这一点。
我使用的环境是 vite,只引入了我们题目的代码,编译结果如下:
var a = 0;
if (true) {
let a2 = function() {
return 3;
};
var a = a2; // 这里会导致最终 a 是个 函数
a2 = 1;
a2 = 2;
console.log(a2);
}
console.log(a);
我们发现由于编译的介入,函数已经被转换成了表达式,而且命名也被替换了,这一没有必要单步执行,按照一行一行执行,结果是 2 和 function 的打印。
我们直接在控制台下输入:
debugger
var a = 0;
if (true) {
a = 1;
function a() {return 3}
a = 2;
console.log(a);
}
console.log(a);
safari 做了函数的提升,最早 a 是一个 function。
执行完 var a = 0
, a 变成 0。
后续两次打印是 a 都是 2。
我们把同样的代码粘贴到 chrome 控制台运行。
刚进入 if 时,global 下的 a 是 0,block 下的 a 是 function。
执行到第一次输出时,global 下的 a 是 1,block 下的 a 是 2。这里不知道什么时候全局下的 a 变成 1 的,深究下去也没有意义,这也正是不符合我们直觉的地方。
所以最后的输出是 2 和 1。
同样的代码,我们只加上严格模式:
'use strict';
debugger;
var a = 0;
if (true) {
a = 1;
function a() {return 3}
a = 2;
console.log(a);
}
console.log(a);
同样的方式,查看调用栈,严格模式下,global 下的 a 不会莫名地变成 1,依然保持 0。
所以输出 2 和 0。
纠结输出什么其实没有意义,这道题在我的实验环境下就有 4 中输出。
给我们的唯一作用就是警示我们写出符合规范的代码。